﻿--[[
   @brief    ui utility functions
   @author   sogoodchen;xiaokunyang;hqzheng
   @version  1.0
   @date     2016-01-08
--]]

--[[
注意：
因编码原因（lua脚本是utf8编码，TPF是utf16编码），
lua与TPF（包括自定义控件）做字符串交互时要做一层转换。
可以使用以下帮助函数进行：
1. utf8 转 LPCWSTR: U8toCUIStr()
   例子： local const_ui_str, str_holder = U8toCUIStr(u8_str);
2. utf8 转 LPWSTR: U8toUIStr()
   例子： local ui_str, mem_holder = U8toUIStr(u8_str);
3. utf8 转 local编码: U8toLoc()
   例子： local loc_str = U8toLoc(u8_str);
4. LPCWSTR 转 utf8： CUIStrtoU8()
   例子： local u8_str = CUIStrtoU8(const_ui_str);
5. LPWSTR 转 utf8： UIStrtoU8()
   例子： local u8_str = UIStrtoU8(ui_str);
6. loc编码 转 utf8： LoctoU8()
   例子： local u8_str = LoctoU8(loc_str);
7. 申请一段UI编码的内存:  NewUIStr  
   例子: local ui_str, mem_holder = NewUIStr(260);
8. 申请一段普通字符内存:  NewStr
   例子: local str = NewStr(260);
--]]

ui_utility = 
{
    main_uilib = nil;
    ctrl_type_infos = {}; -- type_id => type_interface
    wnd_opts = {};
    current_modal_dlg = 0;

    kTCONID_DIALOG = 289;

    -- brief: 获得UILib
    -- return: uilib
    uilib = function()
        if ui_utility.main_uilib ~= nil then
            return ui_utility.main_uilib;
        end;
        local tpfui =
            base_utility.get_component_adapter("ITpf_ui",
                "ierd_tgp.tpf_ui.GetTPFUI();");

        if tpfui ~= nil then
            ui_utility.main_uilib = tpfui:GetUILib();
        else 
            ui_utility.inner_log("ui_utility GetTPFUI is null", true);
        end
        return ui_utility.main_uilib;
    end;

    -- brief: 通过控件相对路径给控件设置属性
    -- parem: parent_id     父控件id
    -- param: relative_path 控件相对位置
    -- param: property_name  属性名（见编辑器属性面板）, utf8
    -- param: property_value 属性值（格式与编辑器对应属性值相同）, utf8
    -- return: 
    setproperty_by_path = function(parent_id, relative_path, property_name, property_value)  
        local control_id = ui_utility.get_ctrl_id_by_path(parent_id, relative_path);
        if ierd_tgp.common.GetLoggerLevel() ~= 0 and
                (control_id == nil or control_id == Tenio.INVALID_CONTROLID) then
            -- 对正式版本发生id错误时，直接返回
            -- 因为继续执行会发生脚本错误，导致setproperty外的脚本无法执行，造成更大错误。
            ui_utility.inner_log(
                "setproperty_by_path failed, parent_id=" .. tostring(id) ..
                ",path=" .. relative_path);
            return;
        end; 
        return ui_utility.setproperty_by_id(control_id, property_name, property_value);
    end;

    -- brief: 通过控件ID给控件设置属性
    -- param: id 控件ID
    -- param: property_name  属性名（见编辑器属性面板）, utf8
    -- param: property_value 属性值（格式与编辑器对应属性值相同）, utf8
    -- return: 
    setproperty_by_id = function(id, property_name, property_value)
        if nil == id or Tenio.INVALID_CONTROLID == id or
            nil == property_name or nil == property_value then
            if 0 ~= ierd_tgp.common.GetLoggerLevel() then
                -- 对正式版本发生id错误时，直接返回
                -- 因为继续执行会发生脚本错误，导致setproperty外的脚本无法执行，造成更大错误。
                ui_utility.inner_log("setproperty_by_id failed, id=" .. tostring(id) ..
                    ", property_name=" .. tostring(property_name) ..
                    ", property_value=" .. tostring(property_value));
            end;  
            return;
        end
        local uilib = ui_utility.uilib();
        return SetProperty_ID(uilib, id, property_name, property_value);
    end;
    
    -- brief: 通过控件ID给控件设置属性
    -- param: custom_id 自定义控件ID
    -- param: property_name  属性名（见编辑器属性面板）, utf8
    -- param: property_value 属性值（格式与编辑器对应属性值相同）, utf8
    -- return: 
    setproperty_by_cid = function(custom_id, property_name, property_value) 
        if ierd_tgp.common.GetLoggerLevel() ~= 0 and 
                (custom_id == nil or custom_id == 0) then
            -- 对正式版本发生id错误时，直接返回
            -- 因为继续执行会发生脚本错误，导致setproperty外的脚本无法执行，造成更大错误。
            ui_utility.inner_log("setproperty_by_cid failed, custom_id=" .. tostring(custom_id));
            return;
        end;
        local uilib = ui_utility.uilib();
        return SetProperty_CID(uilib, custom_id, property_name, property_value);
    end;


    -- brief: 通过相对路径获取控件ID
    -- param: relative_id 相对控件ID，可以是某个根控件
    -- param: relative_path 相对relative_id控件的子路径：utf8
    --        比如有控件名为"d"的控件的全路径：root\a\c\d，则控件"d"相对于控件"a"的relative_path就是"c\d"
    --        调用代码为：local d_id = ui_utility.get_ctrl_id_by_path(a_id, "c\d");
    -- return: 控件ID, 控件不存在则返回Tenio.INVALID_CONTROLID
    get_ctrl_id_by_path = function(relative_id, relative_path)
        local const_ui_str, str_holder = U8toCUIStr(relative_path);
        local uilib = ui_utility.uilib();
        if uilib == nil then
            ui_utility.inner_log(string.format(
            "get_element FAIL, uilib is nil, relative_id:%d, relative_path:%s",
            relative_id, relative_path), true);
            return Tenio.INVALID_CONTROLID;
        end
        local ctrl_id = uilib:GetControlIDbyPath(const_ui_str, relative_id);
        return ctrl_id;
    end;

    -- brief: 获取控件的父节点id
    -- param: sub_id 子节点
    get_ctrl_parent_id = function(sub_id)
        if sub_id == nil then
            return Tenio.INVALID_CONTROLID;
        end
        
        local uilib = ui_utility.uilib();
        if uilib == nil then
            ui_utility.log(string.format(
            "get_element FAIL, uilib is nil, sub_id:%d",
            sub_id), true);
            return Tenio.INVALID_CONTROLID;
        end
        local s_ptr = uilib:GetControlPtr(sub_id);
        local p_ptr = nil;
        if s_ptr ~= nil then
            p_ptr = s_ptr:GetParent();
        end
        local p_id = nil;
        if p_ptr ~= nil then
            p_id = p_ptr:GetControlID();
        end
        return p_id;
    end;

    -- brief: 获取customid对应控件的control(copy from MainCommon, by xiaokun)
    -- param: custom_id
    -- param: interface_type
    -- return: param str, return nil if param not exist
    --[[get_ctrl_ptr = function(custom_id, interface_type)
        local uilib = ui_utility.uilib();
        local ControlId = uilib:GetControlIDbyCustomID(custom_id);
        local pControl = uilib:GetControlPtr(ControlId);
        if pControl == nil then
            base_utility.log(string.format("get_ctrl_ptr FAIL, pControl == nil, id:%d, (%s)",
                custom_id, interface_type))
            return nil;
        end;
        
        local pControlSpecial = tolua.cast(pControl, interface_type);
        if pControlSpecial == nil then
            base_utility.log(string.format("get_ctrl_ptr FAIL, pControlSpecial == nil, %d, (%s)",
               custom_id, interface_type))
            return nil;
        end;
        
        return pControlSpecial;
    end; ]]--
    
    -- brief: 获取控件id对应类型名
    -- param: id 控件ID
    -- return: 控件对象, id非法时返回""
    get_type_name_by_id = function(id)
        local uilib = ui_utility.uilib();
        local pcontrol = uilib:GetControlPtr(id);
        if(pcontrol == nil) then
            return "";
        end;
        
        local interface_type = '';
        local type_id = pcontrol:GetTypeID();
        if (type_id <= 0) then
            return "";
        end;

        if (ui_utility.ctrl_type_infos[type_id] == nil) then
            local interface_type_cuistr = pcontrol:GetInterfaceName();
            interface_type = 'Tenio::'..CUIStrtoU8(interface_type_cuistr);
            ui_utility.ctrl_type_infos[type_id] = interface_type;
        else
            interface_type = ui_utility.ctrl_type_infos[type_id];
        end;
        return interface_type;
    end;

    -- brief: 获取控件id对应控件对象
    -- param: id 控件ID
    -- return: 控件对象, id非法时返回nil
    get_ctrl_ptr_by_id = function(id)
        local interface_type = ui_utility.get_type_name_by_id(id);
        if interface_type == "" then
            return nil;
        end
        local uilib = ui_utility.uilib();
        local pcontrol = uilib:GetControlPtr(id);
        if(pcontrol == nil) then
            return nil;
        end;
        local ctrl_ptr = tolua.cast(pcontrol, interface_type);
        if(ctrl_ptr == nil) then
            return nil;
        end;
        return ctrl_ptr;
    end;
    
    -- brief: 获取控件id对应控件对象
    -- param: custom_id 用户自定义ID
    -- return: 控件对象, custom_id非法时返回nil
    get_ctrl_ptr_by_cid = function(custom_id)
        local uilib = ui_utility.uilib();
        local id = uilib:GetControlIDbyCustomID(custom_id);
        if (id == Tenio.INVALID_CONTROLID) then
            return nil;
        end;
        return ui_utility.get_ctrl_ptr_by_id(id);
    end;

    -- brief: 获取控件custom_id对应控件id
    -- param: custom_id 用户自定义ID
    -- return: 控件id, custom_id非法时返回Tenio.INVALID_CONTROLID
    get_ctrl_id_by_cid = function(custom_id)
        local uilib = ui_utility.uilib();
        local id = uilib:GetControlIDbyCustomID(custom_id);
        return id;
    end;

    -- brief: 通过相对路径获取控件指针
    -- param: relative_id 相对控件ID，可以是某个根控件
    -- param: relative_path 相对relative_id控件的子路径：utf8
    --        比如有控件名为"d"的控件的全路径：root\a\c\d，则控件"d"相对于控件"a"的relative_path就是"c\d"
    --        调用代码为：local d_ptr = ui_utility.get_ctrl_ptr_by_path(a_id, "c\d");
    -- return: 控件指针, 控件不存在则返回nil
    get_ctrl_ptr_by_path = function(relative_id, relative_path)
        local ctrl_id= ui_utility.get_ctrl_id_by_path(relative_id, relative_path);
        local ptr = ui_utility.get_ctrl_ptr_by_id(ctrl_id);
        return ptr;
    end;

    -- brief: 获取控件的节点路径
    -- param: id 控件ID
    -- return: 节点路径字符串：utf8
    get_ctrl_path = function(id)
        local uilib = ui_utility.uilib();
        local ui_str, mem_holder = NewUIStr(260);
        uilib:GetControlFullPath(id, ui_str, 260);
        local u8_str = UIStrtoU8(ui_str);
        return u8_str;
    end;

    -- brief: 设置对话框移动跟随
    -- param: src_dlg_cid - 要跟随别人的dialog控件的CustomID 
    -- param: dst_dlg_cid - 被别人跟随的dialog控件的CustomID 
    -- param: offset_x - src_dlg相对于dst_dlg的横坐标偏移（dst_dlg坐标系）
    -- param: offset_y - src_dlg相对于dst_dlg的纵坐标偏移（dst_dlg坐标系）
    -- return: 操作成功与否(TRUE/FALSE)
    set_move_follow = function(src_dlg_cid, dst_dlg_cid, offset_x, offset_y)
        local src_dlg = ui_utility.get_ctrl_ptr_by_cid(src_dlg_cid);
        if src_dlg == nil then
            return;
        end;

        local dst_dlg_id = Tenio.INVALID_CONTROLID;
        local dst_dlg = ui_utility.get_ctrl_ptr_by_cid(dst_dlg_cid);
        if dst_dlg ~= nil then
            dst_dlg_id = dst_dlg:GetControlID();
        end;
        
        return src_dlg:MoveFollow(dst_dlg_id, offset_x, offset_y);
    end;
    
    -- brief: 窗口跟随
    -- param: listener_ctrl_cid - 跟随窗口的custom id
    -- param: op_ctrl_cid - 被跟随窗口的custom id
    -- return: 无
    follow_wnd = function(listener_ctrl_cid, op_ctrl_cid)
        local listener_ctrl = ui_utility.get_ctrl_ptr_by_cid(listener_ctrl_cid);
        local op_ctrl = ui_utility.get_ctrl_ptr_by_cid(op_ctrl_cid);
        if listener_ctrl ~= NULL and op_ctrl ~= NULL then
            if op_ctrl:IsVisible() ~= TRUE then -- 避免restore_layout时触发，需把dialog设为不可见。
                return;
            end;
            
            -- 统一使用屏幕坐标
            local listener_ctrl_rc = listener_ctrl:GetRectEx(TRUE,TRUE);
            local op_ctrl_rc = op_ctrl:GetRectEx(TRUE,TRUE);
            
            if op_ctrl_rc.left == op_ctrl_rc.right then -- 忽略最小化
                return;
            end;
            if listener_ctrl_rc.left < -30000 or listener_ctrl_rc.right < -30000 then -- 忽略最小化
                return;
            end;
            if op_ctrl_rc.left < -30000 or op_ctrl_rc.right < -30000 then -- 忽略最小化
                return;
            end;

            local listener_ctrl_width = listener_ctrl_rc.right - listener_ctrl_rc.left;
            local listener_ctrl_height = listener_ctrl_rc.bottom - listener_ctrl_rc.top;
            
            local listener_ctrl_new_left = 0;
            local listener_ctrl_new_top = 0;
            
            -- 计算跟随窗口的左右关系
            -- 1-在右边，2-在左边。
            local listener_custom_data = listener_ctrl:GetCustomData();
            local op_custom_data = op_ctrl:GetCustomData();
            local listener_on_left = false;
            if listener_custom_data == 1 then
                listener_on_left = false;
            elseif listener_custom_data == 2 then
                listener_on_left = true;
            end;
            if op_custom_data == 1 then
                listener_on_left = true;
            elseif op_custom_data == 2 then
                listener_on_left = false;
            end;
            
            if listener_on_left then
                listener_ctrl_new_left = op_ctrl_rc.left - listener_ctrl_width;
            else
                listener_ctrl_new_left = op_ctrl_rc.right;
            end;
            listener_ctrl_new_top = op_ctrl_rc.top;
            
            if listener_ctrl_new_left ~= listener_ctrl_rc.left or listener_ctrl_new_top ~= listener_ctrl_rc.top then
                -- 如果是扩展界面（其是main_wnd的子窗口），需要把屏幕坐标转换成窗口坐标。
                if listener_ctrl_cid ~= CID_MAINFRAME_Dialog_MainFrame then
                    listener_ctrl_new_left = listener_ctrl_new_left - op_ctrl_rc.left;
                    listener_ctrl_new_top = listener_ctrl_new_top - op_ctrl_rc.top;
                end;
                
                listener_ctrl_rc.left = listener_ctrl_new_left;
                listener_ctrl_rc.right = listener_ctrl_new_left + listener_ctrl_width;
                listener_ctrl_rc.top = listener_ctrl_new_top;
                listener_ctrl_rc.bottom = listener_ctrl_new_top + listener_ctrl_height;
                
                listener_ctrl:MoveEx(listener_ctrl_rc,TRUE);
            end;
        end;
    end;
    
    -- brief: 启动假模态
    -- param: modal_dlg_id - 模态对话框的控件自定义ID
    -- return: 无
    -- note:  此假模态将disable根控件下的其他对话框，BringToTop并显示自己
    --        模态对话框的布局应该用layout_mgr.restore_layout恢复
    --        建议将在编辑器里面将模态对话框的AlwaysOnTop属性设置为true
    do_fake_modal_by_id = function(modal_dlg_id)
        ui_utility.inner_log("do_fake_modal, id = ".. modal_dlg_id);
        local pdlg = ui_utility.get_ctrl_ptr_by_id(modal_dlg_id);
        if (pdlg == nil) then
            ui_utility.inner_log('do_fake_modal failed, invalid custom_id='..modal_dlg_id);
            return;
        end;
        local parent_id = ui_utility.get_ctrl_parent_id(modal_dlg_id);  
        local grand_parent =  ui_utility.get_ctrl_parent_id(parent_id);
        base_utility.log('[utility][do_fake_modal]parent_id:' .. tostring(parent_id) ..
                                'grand_parent:' .. tostring(grand_parent));
        local control_ids = layout_mgr.get_existing_dlg_ids();
        for control_id, xxx in pairs(control_ids) do
            if modal_dlg_id == control_id then            
            elseif parent_id == control_id then 
                base_utility.log('[utility][do_fake_modal]disable brothers, parent_id:' ..
                                     tostring(parent_id));
                ui_utility.enable_other_child(parent_id, modal_dlg_id, false);
            elseif grand_parent == control_id then
                base_utility.log('[utility][do_fake_modal]disable parent brothers, grand_parent:' ..
                                     tostring(grand_parent));
                ui_utility.enable_other_child(grand_parent, parent_id, false);
            else
                local other_dlg = ui_utility.get_ctrl_ptr_by_id(control_id);
                if (other_dlg ~= nil) then
                    base_utility.log('[utility][do_fake_modal]disable dlg:' .. tostring(control_id));
                    other_dlg:Enable(FALSE);
                end;                
            end
        end
        pdlg:Show(TRUE);
        pdlg:BringToTop();
        ui_utility.current_modal_dlg = modal_dlg_id;
    end;

    -- brief: 启动假模态
    -- param: modal_dlg_custom_id - 模态对话框的控件ID
    -- return: 无
    -- note:  此假模态将disable根控件下的其他对话框，BringToTop并显示自己
    --        模态对话框的布局应该用layout_mgr.restore_layout恢复
    --        建议将在编辑器里面将模态对话框的AlwaysOnTop属性设置为true
    do_fake_modal = function(modal_dlg_custom_id)
        ui_utility.inner_log("do_fake_modal, id = ".. modal_dlg_custom_id);
        local pdlg = ui_utility.get_ctrl_ptr_by_cid(modal_dlg_custom_id);
        if (pdlg == nil) then
            ui_utility.inner_log('do_fake_modal failed, invalid custom_id='..modal_dlg_custom_id);
            return;
        end;
        local modal_dlg_id = pdlg:GetControlID();
        local control_ids = layout_mgr.get_existing_dlg_ids();
        for control_id, xxx in pairs(control_ids) do
            if modal_dlg_id ~= control_id then
                local other_dlg = ui_utility.get_ctrl_ptr_by_id(control_id);
                if (other_dlg ~= nil) then
                    other_dlg:Enable(FALSE);
                end;                
            end
        end
        pdlg:Show(TRUE);
        pdlg:BringToTop();
        ui_utility.current_modal_dlg = modal_dlg_id;
    end;

    -- brief: 结束假模态
    -- param: modal_dlg_custom_id - 模态对话框的控件自定义ID
    -- return: 无
    -- note:  enable模态时被disable的其他对话框，并隐藏自己
    end_fake_modal = function(modal_dlg_custom_id, top_dlg_cid)
        ui_utility.inner_log("end_fake_modal, id = ".. modal_dlg_custom_id);
        local control_ids = layout_mgr.get_existing_dlg_ids();
        for control_id, xxx in pairs(control_ids) do
            local other_dlg = ui_utility.get_ctrl_ptr_by_id(control_id);
            if (other_dlg ~= nil) then
                other_dlg:Enable(TRUE);
            end;
        end;
        -- 顶层窗口关闭时，window manager会由上至下找个窗口去activate
        -- 如果首先找到的窗口处于disabled状态，将被跳过，因而会出现diabled的窗口突然跑到底层的现象(如工具箱闪烁问题)
        -- 避免这种问题，最好的办法是在关闭fake_modal窗口前先将其他窗口enable
        local pdlg = ui_utility.get_ctrl_ptr_by_cid(modal_dlg_custom_id);
        if (pdlg ~= nil) then
            pdlg:Show(FALSE);
            -- 如果当前的模态框并不是该窗口，return。(避免第一个模态框关闭时，将新出来的模态框置于主界面之下)
            local modal_dlg_id = pdlg:GetControlID();
            if ui_utility.current_modal_dlg ~= modal_dlg_id then
                ui_utility.current_modal_dlg = 0;
                return;
            end
        end;

        ui_utility.current_modal_dlg = 0;

        if top_dlg_cid == nil then
            top_dlg_cid = CID_MAINFRAME_Dialog_MainFrame;
        end;

        local top_wnd = ui_utility.get_ctrl_ptr_by_cid(top_dlg_cid);
        if top_wnd ~= nil and top_wnd:IsVisible() == TRUE then
            top_wnd:BringToTop();
        end;
    end;

    -- brief: 结束假模态
    -- param: modal_dlg_id - 模态对话框的控件ID
    -- return: 无
    -- note:  enable模态时被disable的其他对话框，并隐藏自己
    end_fake_modal_by_id = function(modal_dlg_id, top_dlg_cid, opt_parent_id)
        base_utility.log("[utility] end_fake_modal, id = ".. modal_dlg_id);
        local control_ids = layout_mgr.get_existing_dlg_ids();   
        local parent_id = ui_utility.get_ctrl_parent_id(modal_dlg_id);  
        local grand_parent =  ui_utility.get_ctrl_parent_id(parent_id);
        base_utility.log('[utility][end_fake_modal_by_id]parent_id:' .. tostring(parent_id) ..
                                'grand_parent:' .. tostring(grand_parent));
        for control_id, xxx in pairs(control_ids) do 
            if modal_dlg_id == control_id then            
            elseif parent_id == control_id then 
                base_utility.log('[utility][end_fake_modal_by_id]enable brothers, parent_id:' ..
                                     tostring(parent_id));
                ui_utility.enable_other_child(parent_id, modal_dlg_id, true);
            elseif grand_parent == control_id then
                base_utility.log('[utility][end_fake_modal_by_id]enable parent brothers, grand_parent:' ..
                                     tostring(grand_parent));
                ui_utility.enable_other_child(grand_parent, parent_id, true);
            else
                local other_dlg = ui_utility.get_ctrl_ptr_by_id(control_id);
                if (other_dlg ~= nil) then
                    other_dlg:Enable(TRUE);
                end;
            end;
        end;
        -- 顶层窗口关闭时，window manager会由上至下找个窗口去activate
        -- 如果首先找到的窗口处于disabled状态，将被跳过，因而会出现diabled的窗口突然跑到底层的现象(如工具箱闪烁问题)
        -- 避免这种问题，最好的办法是在关闭fake_modal窗口前先将其他窗口enable
        local pdlg = ui_utility.get_ctrl_ptr_by_id(modal_dlg_id);
        if (pdlg ~= nil) then
            pdlg:Show(FALSE);
        end;

        -- 如果当前的模态框并不是该窗口，return。(避免第一个模态框关闭时，将新出来的模态框置于主界面之下)
        if ui_utility.current_modal_dlg ~= modal_dlg_id then
            ui_utility.current_modal_dlg = 0;
            return;
        end
        ui_utility.current_modal_dlg = 0;
        local parent_wnd = nil;
        if opt_parent_id then
            parent_wnd = ui_utility.get_ctrl_ptr_by_id(opt_parent_id);
            if parent_wnd then 
                base_utility.log('[utility][end_fake_modal_by_id]bring parent to top, opt_parent_id:' ..
                                     tostring(opt_parent_id));
                parent_wnd:BringToTop();
            end;
        end;

        if not parent_wnd then
            if top_dlg_cid == nil then  
                base_utility.log('[utility][end_fake_modal_by_id]will bring main wnd to top');
                top_dlg_cid = CID_MAIN_WINDOW;
            end;

            local top_wnd = ui_utility.get_ctrl_ptr_by_cid(top_dlg_cid);
            if top_wnd ~= nil and top_wnd:IsVisible() == TRUE then  
                base_utility.log('[utility][end_fake_modal_by_id]bring top_wnd to top');
                top_wnd:BringToTop();
            end;
        end;
    end;

    enable_other_child = function(parent_id, skip_child_id, enable)
        base_utility.log("[utility]enable_all_child, parent_id:" .. tostring(parent_id) ..
                    ", skip_child_id:" .. tostring(skip_child_id) ..
                    ", enable:" .. tostring(enable));
                    
        local parent = ui_utility.get_ctrl_ptr_by_id(parent_id);
        if parent == nil then
            base_utility.log("[utility][enable_all_child]parent is nil, parent_id:" .. tostring(parent_id) ..
                    ", enable:" .. tostring(enable), true);
            return false;
        end
        
        local next_child = parent:GetFirstChild();
        while next_child ~= nil do
            local child_id = next_child:GetControlID();
            if child_id ~= skip_child_id then
                if enable then
                    next_child:Enable(TRUE);
                else
                    next_child:Enable(FALSE);
                end                
            else  
                base_utility.log("[utility][enable_all_child]skip " .. tostring(child_id));
            end
            
            -- 上层兄弟就是下一个子控件
            next_child = next_child:GetUpperSibling();
        end
        
        return true;
    end;
    
    --[[
     brief: 显示公共弹窗tips
     param: 详见poptips中的view，不传参数为默认显示样式
     size: WeGame 通用tips尺寸 480*230
    ]]--
    show_tips = function(param)
       poptips_control.show_tips(param);

       local value ={};
        if param ~= nil then
            if param.descTitle ~= nil and param.descTitle ~= "" then 
                value.title = param.descTitle;
            else
                value.title = "";
            end;
            if param.desc ~= nil and param.desc ~= "" then
                value.content = param.desc;
            else
                value.content = "";
            end
            if value ~= nil then
                base_utility.mainframe_report("common_tips", "show", base_utility.table_to_json(value));
            else
                base_utility.mainframe_report("common_tips", "show", "");
            end;
        end;

        return;
    end;

    --[[
     brief: 显示公共web弹窗
     param: 详见poptips中的view，不传参数为默认显示样式
    ]]--
    show_web = function(param)
        return poptips_control.show_web(param);
    end;

    -- brief:通过路径获得控件尺寸
    -- param:parent id, relative path
    -- return:width,height
    get_control_size_by_path = function (parent_id, relative_path)
        local control_id = 0;
        control_id = ui_utility.get_ctrl_id_by_path(parent_id, relative_path);
        return ui_utility.get_control_size_by_id(control_id);
    end;

    -- brief:通过id获得控件尺寸
    -- param:control id 
    -- return:width,height
    get_control_size_by_id = function (control_id)
        local control_rect = nil;
        local control_ptr = ui_utility.get_ctrl_ptr_by_id(control_id);
        if control_ptr ~= nil then
            control_rect = control_ptr:GetRectEx(FALSE,TRUE);
        end
        local control_width = 0;
        local control_height = 0;
        if control_rect ~= nil then
            control_width = control_rect.right - control_rect.left;
            control_height = control_rect.bottom - control_rect.top;
        end
        return control_width, control_height;
    end;

    -- brief:通过id改变控件尺寸
    -- param:control id, width(nil 则不改变), height(nil则不改变)
    -- return:null
    control_resize_by_id = function (control_id, width, height)
        ui_utility.inner_log(
            "control_resize_by_id, control_id" .. control_id);
        if width ~= nil and width >= 0 then
            ui_utility.inner_log(
            "control_resize_by_id, width" .. width);
            ui_utility.setproperty_by_id(control_id, "Width", width);
        end
        if height ~= nil and height >= 0 then
            ui_utility.inner_log(
            "control_resize_by_id, height" .. height);
            ui_utility.setproperty_by_id(control_id, "Height", height);
        end
    end;

    -- brief:通过path改变控件尺寸
    control_resize_by_path = function (parent_id, path, width, height)
       if parent_id == nil then
            ui_utility.inner_log("[control_resize_by_path]parent_id is nil");
            return;
       end
       local control_id = 0;
       control_id = ui_utility.get_ctrl_id_by_path(parent_id, path);
       ui_utility.control_resize_by_id(control_id, width, height);
    end;

    -- brief: change web area size
    -- param: parent id, relative path, width, height
    -- return: null
    qbweb_setsize_by_path = function (parent_id, relative_path, width, height)
        local qbwebctn_id = ui_utility.get_ctrl_id_by_path(parent_id, relative_path);
        local qbwebctn_ptr = ui_utility.get_ctrl_ptr_by_id(qbwebctn_id);
        if qbwebctn_ptr == nil then
            ui_utility.inner_log("change_qbweb_size_by_path qbwebctn_ptr is nil");
            return;
        end
        local qbweb_ptr = tolua.cast(qbwebctn_ptr, "Tenio::IQBWebBrowser");
        if qbweb_ptr == nil then
            ui_utility.inner_log("change_qbweb_size_by_path qbweb_ptr is nil");
            return;
        end

        local qbweb_rect = qbwebctn_ptr:GetRectEx(FALSE,TRUE);

        if qbweb_rect == nil then
            ui_utility.inner_log("change_qbweb_size_by_path qbweb_rect is nil");
            return;
        end

        local qbweb_new_rect = qbweb_rect;
        qbweb_new_rect.left = qbweb_rect.left;
        qbweb_new_rect.right = qbweb_rect.left + width;
        qbweb_new_rect.top = qbweb_rect.top;
        qbweb_new_rect.bottom = qbweb_rect.top + height;

        qbweb_ptr:SetSize(qbweb_new_rect);
    end;

    -- brief:父控件宽度变化，子控件自动调整宽度（父控件dock排布，子控件宽度一样）
    -- param:parent id, children id list
    -- return:null 
    layout_children_by_id = function (parent_id, children_id_list)
        ui_utility.inner_log("layout_children_by_id");
        if parent_id == nil then
            ui_utility.inner_log("layout_children_by_id parent_id is nil");
            return;
        end
        
        local parent_width = nil;
        local parent_height = nil;

        parent_width, parent_height = ui_utility.get_control_size_by_id(parent_id);

        if children_id_list == nil then
            ui_utility.inner_log("layout_children_by_id, children_id_list is nil");
            return;
        end

        local count = table.getn(children_id_list);

        if  count == nil or count == 0 then
            ui_utility.inner_log("layout_children_by_id, children_id_list table is empty");
            return;
        end

        local child_width = parent_width / count;

        ui_utility.inner_log("layout_children_by_id, child_width is " .. child_width);

        table.foreach(children_id_list, 
            function (i, v)
                ui_utility.inner_log("layout_children_by_id current child_id is " ..  v);
                ui_utility.control_resize_by_id(v, child_width);
            end);
    end;

    --brief:get control rect
    --param:parent id, relative path
    --return:rect
    get_rect = function (parent_id, relative_path)
        local ctrl_ptr = ui_utility.get_ctrl_ptr_by_path(parent_id, relative_path);
        if ctrl_ptr == nil then
            ui_utility.inner_log("get_rect, ctr_ptr is nil");
            return nil;
        end

        local  rect = ctrl_ptr:GetRectEx(FALSE,TRUE);
        return rect;
    end;

    --brief:显示指定相对路径的的孩子，其他孩子不显示，如果没有指定则所有孩子都不显示
    --param:parent id , 需要显示的孩子节点的相对路径
    --return:是否成功显示指定的孩子
    show_only_child = function (parent_id, name_toshow)
        ui_utility.inner_log(string.format("[show_only_child], parent_id:%d, name_toshow:%s",
            parent_id, tostring(name_toshow)));

        local MAX_NAME = 260;
        local ui_str, mem_holder = NewUIStr(MAX_NAME);
        local parent = ui_utility.get_ctrl_ptr_by_id(parent_id);
        if parent == nil then
            ui_utility.inner_log(string.format(
                "[show_only_child]parent is NIL, parent_id:%d," ..
                " name_toshow:%s", parent_id, tostring(name_toshow)));
            return false;
        end
        local any_show = false;
        local next_child = parent:GetFirstChild();
        while next_child ~= nil do
            next_child:GetControlName(ui_str, MAX_NAME);
            local name = UIStrtoU8(ui_str);
            if name == name_toshow then
                ui_utility.inner_log(string.format("[show_only_child]found it, show it"));
                next_child:Show(TRUE);
                any_show = true;
            else
                next_child:Show(FALSE);
            end
        
            -- 上层兄弟就是下一个子控件
            next_child = next_child:GetUpperSibling();
        end
    
        return any_show;
    end; 

    --brief:显示/隐藏指定相对路径的的孩子
    --param:parent id , name_path(需要显示的孩子节点的相对路径)
    --return:是否成功显示指定的孩子
    show_child = function (parent_id, name_path, show)
        ui_utility.inner_log(string.format("show_child, parent:%d, child:%s, show:%s",
           parent_id, name_path, tostring(show)));
        local pControl = ui_utility.get_ctrl_ptr_by_path(parent_id, name_path);
        if pControl == nil or pControl == NULL then
            ui_utility.inner_log(string.format("show_child, pControl is NIL, parent:%d, child:%s",
               parent_id, name_path), true);
            return false;
        end
    
        if show then
            pControl:Show(TRUE);
        else
            pControl:Show(FALSE);
        end
            
        return true;
    end;

    --brief:设置指定相对路径的孩子控件的可用性
    --param:parent id, relative_path, is_enable(是否可用)
    --return:是否设置
    set_enable = function (parent_id, relative_path, is_enable)
        local ctrl_id = ui_utility.get_ctrl_id_by_path(parent_id, relative_path);
        ui_utility.set_enable_by_id(ctrl_id, is_enable);
    end;

    set_enable_by_id = function (control_id, is_enable)

        local ctrl_ptr = ui_utility.get_ctrl_ptr_by_id(control_id);

        if ctrl_ptr == nil then 
            ui_utility.inner_log("[set_enable_by_id] ctrl_ptr is nil");
            return false;
        end

        if is_enable then
            ctrl_ptr:Enable(TRUE); 
        else 
            ctrl_ptr:Enable(FALSE);
        end

        return true;
    end;

    -- brief:设置控件可见性
    -- param: control_id, is_visible(是否可见)
    -- return: null
    set_visible = function (control_id, is_visible)
        ui_utility.inner_log(string.format("[set_visible], control_id:%d, visible:%s",
          control_id, tostring(is_visible)));
        local control_ptr = ui_utility.get_ctrl_ptr_by_id(control_id);
        if control_ptr then
            if is_visible then
                control_ptr:Show(TRUE);
            else
                control_ptr:Show(FALSE);
            end
        else
            ui_utility.inner_log("[set_visible]control_ptr is NIL");
        end
    end;
     
    is_visible = function (control_id)
        local control_ptr = ui_utility.get_ctrl_ptr_by_id(control_id);
        if control_ptr then 
            local visible =  control_ptr:IsVisible();
            if TRUE == visible then
                return true;
            end
        else
            ui_utility.inner_log("[is_visible]control_ptr is NIL, id:" .. tostring(control_id));
        end

        return false;
    end;

    -- 判断ancestry是否是progeny的祖先
    is_ancestry = function(ancestry_id, progeny_id)
        if Tenio.INVALID_CONTROLID == ancestry_id or
           Tenio.INVALID_CONTROLID == progeny_id then
                return false;
        end 

        local child_id = progeny_id;
        repeat
            local parent_id = ui_utility.get_ctrl_parent_id(child_id);
            if parent_id == ancestry_id then
                return true;
            end
            child_id = parent_id;
        until (Tenio.INVALID_CONTROLID == child_id or 
              Tenio.INVALID_CONTROLID == parent_id);

        return false;
    end;

    -- brief:根据原来的rect和移动到的new_pos生成新的rect
    -- param: rect, new_pos
    -- return: new rect
    move_rect = function (rect, new_pos)
        local str = string.format("%d,%d; %d,%d", rect.left, rect.top, rect.right, rect.bottom);
        ui_utility.inner_log(string.format("move_rect (%s) to new pos:(%d,%d)",
            str, new_pos.x, new_pos.y));
        local width = rect.right - rect.left;
        local height = rect.bottom - rect.top;
        rect.left = new_pos.x;
        rect.top = new_pos.y;
    
        rect.right = rect.left + width;
        rect.bottom = rect.top + height;
    
        return rect;
    end;

    show_ctrl = function(control_id, show)
        local ctrl = ui_utility.get_ctrl_ptr_by_id(control_id);
        if ctrl == nil then
            ui_utility.inner_log("[show_ctrl]ctrl is nil", true);
            return false;
        end
        
        if show then
            ctrl:Show(TRUE);
        else
            ctrl:Show(FALSE);
        end
        
        return true;
    end;

    on_win_status = function(is_maximized_state)
        local prestore_btn = ui_utility.get_ctrl_ptr_by_cid(CID_ACTIVITY_DETAIL_RESTORE_BTN);
        local pmax_btn = ui_utility.get_ctrl_ptr_by_cid(CID_ACTIVITY_DETAIL_MAX_BTN);
        if prestore_btn == nil or pmax_btn == nil then
            return;
        end;

        if is_maximized_state then
            prestore_btn:Show(TRUE);
            pmax_btn:Show(FALSE);
        else
            prestore_btn:Show(FALSE);
            pmax_btn:Show(TRUE);
        end;
    end;

    on_restore = function(uilib, id)
        ui_utility.on_win_status(false);
    end;

    on_maximize = function(uilib, id)
        ui_utility.on_win_status(true);
    end;

    on_removing_layout = function(event_id, json_str)    
        local json_params = base_utility.json_to_table(json_str);
        if nil == json_params or nil == json_params.id then
            return;
        end
        
        if ui_utility.is_ancestry(json_params.id, ui_utility.current_modal_dlg) then   
            base_utility.log("[ui_utility][on_removing_layout]removing modal dlg's ancestry " ..
                tostring(json_params.id) ..
                ", cur modal:" .. tostring(ui_utility.current_modal_dlg));
            ui_utility.end_fake_modal_by_id(ui_utility.current_modal_dlg); 
        end        
    end;

    enable_all_dialog = function(enable)
        local control_ids = layout_mgr.get_existing_dlg_ids();
        for control_id, xxx in pairs(control_ids) do
            local dlg = ui_utility.get_ctrl_ptr_by_id(control_id);
            if dlg ~= nil then
                dlg:Enable(enable);
            end;
        end;
    end;

    -- brief:set static text
    -- param: parent_id, relative path, text
    -- return: setting success or not
    set_static_text = function (parent_id, child_path, text_str)
        if nil == parent_id or nil == child_path or nil == text_str then
            ui_utility.inner_log("param is nil");
            return;
        end
        local static_ptr = ui_utility.get_ctrl_ptr_by_path(parent_id, child_path);
        if nil ~= static_ptr then
            ui_utility.inner_log(string.format(
                "[set_static_text]set text, child_path:%s, text:%s",
                child_path, text_str));
        
            local str = U8toCUIStr(text_str);
        
            static_ptr:SetText(str);
            return true;
        else
            ui_utility.inner_log(string.format(
                "[set_static_text]static_ptr is nil, child_path:%s, text:%s",
                child_path, text_str), true);
            return false;
        end
    end;

    -- 获取当前屏幕分辨率
    get_screen_resolution = function ()
        local cur_resolution_x = 0;
        local cur_resolution_y = 0;
        local cur_work_area_x = 0;
        local cur_work_area_y = 0;
        local tpf_ui =
            base_utility.get_component_adapter("ITpf_ui",
                "ierd_tgp.tpf_ui.GetTPFUI();");
        if nil == tpf_ui then
            return cur_resolution_x, cur_resolution_y;
        end

        local sys_param = ierd_tgp.tpf_ui.SysParams:new();
        tpf_ui:get_sys_params(sys_param);
        cur_resolution_x = sys_param.screen_width;
        cur_resolution_y = sys_param.screen_height;
        cur_work_area_x = sys_param.work_area_width;
        cur_work_area_y = sys_param.work_area_height;
        sys_param:delete();
        ui_utility.inner_log(string.format(
            "[get_screen_resolution]resolution_x:%d,resolution_y:%d,work_area_x:%d,work_area_y:%d",
            cur_resolution_x, cur_resolution_y, cur_work_area_x, cur_work_area_y));
        return cur_resolution_x, cur_resolution_y, cur_work_area_x, cur_work_area_y;
    end;

    set_common_menu_top_item_bkg = function (menu_item_id)
        ui_utility.setproperty_by_id(menu_item_id, "Background",
            "NONE");
        ui_utility.setproperty_by_id(menu_item_id, "BackgroundHover",
        "9IMAGES:client_mainframe\\nav_drop_new.png?52,1,61,10;" ..
        "client_mainframe\\nav_drop_new.png?62,1,71,10;" ..
        "client_mainframe\\nav_drop_new.png?72,1,81,10;" ..
        "client_mainframe\\nav_drop_new.png?52,11,61,20;" ..
        "client_mainframe\\nav_drop_new.png?62,11,71,20;" ..
        "client_mainframe\\nav_drop_new.png?72,11,81,20;" ..
        "client_mainframe\\nav_drop_new.png?52,21,61,30;" ..
        "client_mainframe\\nav_drop_new.png?62,21,71,30;" ..
        "client_mainframe\\nav_drop_new.png?72,21,81,30;Stretch;0;");
        ui_utility.setproperty_by_id(menu_item_id, "BackgroundPressed", "NONE");
    end;

    set_common_menu_bottom_item_bkg = function (menu_item_id)
        ui_utility.setproperty_by_id(menu_item_id, "Background", "NONE");
        ui_utility.setproperty_by_id(menu_item_id, "BackgroundHover",
        "9IMAGES:client_mainframe\nav_drop_new.png?88,1,97,10;" .. 
        "client_mainframe\\nav_drop_new.png?98,1,107,10;" ..
        "client_mainframe\\nav_drop_new.png?108,1,117,10;" ..
        "client_mainframe\\nav_drop_new.png?88,11,97,20;" ..
        "client_mainframe\\nav_drop_new.png?98,11,107,20;" ..
        "client_mainframe\\nav_drop_new.png?108,11,117,20;" ..
        "client_mainframe\\nav_drop_new.png?88,21,97,30;" ..
        "client_mainframe\\nav_drop_new.png?98,21,107,30;" ..
        "client_mainframe\\nav_drop_new.png?108,21,117,30;Stretch;0;");
        ui_utility.setproperty_by_id(menu_item_id, "BackgroundPressed", "NONE");
    end;

    set_common_menu_nor_item_bkg = function (menu_item_id)
        ui_utility.setproperty_by_id(menu_item_id, "Background", "NONE");
        ui_utility.setproperty_by_id(menu_item_id, "BackgroundHover", 
            "ONEIMAGE:client_mainframe\\nav_drop_new.png?125,1,134,10;Stretch;0");
        ui_utility.setproperty_by_id(menu_item_id, "BackgroundPressed", "NONE");
    end;


    -- 内部使用的帮助函数，用于log增加本模块的filter
    inner_log = function (msg, core_msg)
        base_utility.log(string.format("[ui_utility] %s", msg), core_msg);
    end;

    -- brief:检测utf-8字符串(包括中文)是否超过规定字符数
    -- param: str, max_char_count
    -- return: 超过字符数返回true，否则false
    is_over_char_count = function (str, max_char_count)
        if str == nil or str == "" then
            return false;
        end
        if max_char_count == nil or max_char_count < 0 then
            return false;
        end
        local cur_char_count = 0;
        local sub_pos = 1;
        local one_byte = "";
        local is_over = false;
        local len_str = #str
        if len_str ~= nil then
            if len_str <= max_char_count then
                is_over = false;
            else
                for i=1,len_str do
                    one_byte = string.byte(str,sub_pos);
                    if(one_byte == nil) then 
                        is_over = false;
                        break;
                    end
                    local byte_count = 1;
                    if one_byte > 0 and one_byte < 127 then
                        byte_count = 1;
                    elseif one_byte >= 192 and one_byte < 223 then 
                        byte_count = 2;
                    elseif one_byte >= 224 and one_byte < 239 then 
                        byte_count = 3;
                    elseif one_byte >= 240 and one_byte <= 247 then 
                        byte_count = 4;
                    end
                    cur_char_count= cur_char_count + 1;
                    sub_pos = (sub_pos + byte_count);
                    if cur_char_count == max_char_count then
                        local next_byte = string.byte(str,sub_pos);
                        if next_byte == nil then
                            is_over = false;
                            sub_pos = 0;
                        else
                            is_over = true;
                        end
                        break;
                    end
                end
            end
        end
        return is_over;
    end;

    -- oss返回的背景颜色值134092(RGB十六进制字符串)转换为"COLOR:RGB:19,64,146"类型的格式
    convert_hex_color_to_str = function(hex_str_param)
        local str_color = "";
        if hex_str_param == nil or hex_str_param == "" then
            return str_color;
        end
        local str_hex = base_utility.trim(tostring(hex_str_param));
        local red_str = string.sub(str_hex, 1, 2);
        local greed_str = string.sub(str_hex, 3, 4);
        local blue_str = string.sub(str_hex, 5, -1);
        local dec_red = tonumber(red_str, 16);
        local dec_greed = tonumber(greed_str, 16);
        local dec_blue = tonumber(blue_str, 16);
        if dec_red ~= nil and dec_greed ~= nil and dec_blue ~= nil then
            str_color = "COLOR:RGB:" .. dec_red ..','..dec_greed .. ','.. dec_blue;
        end
        return str_color;
    end;

    cal_str_width = function (str)
        local width = string.len(str);
        local str_id = ui_utility.get_ctrl_id_by_path(wegame_mainframe_view.dialog_id,
            "txt_cal_str_width");
        if 0 == str_id then
            return width;
        end
        ui_utility.setproperty_by_id(str_id, "Caption", str);
        local cal_width = ui_utility.get_control_size_by_id(str_id);
        if 0 ~= cal_witdh  then
            width = cal_width;
        end
        return width;
    end;

    get_dpi_scale = function ()
        local dpi_scale = 1.0;
        local tpf_ui =
            base_utility.get_component_adapter("ITpf_ui",
                "ierd_tgp.tpf_ui.GetTPFUI();");
        if nil ~= tpf_ui then
            dpi_scale = tpf_ui:GetDPIScale();
        end
        return dpi_scale;
    end;

    show_bp_launch_error_tips = function(game_id, game_name, err_code, err_msg)
        local param = {};
        param.game_name = game_name;
        param.game_id = tostring(game_id);
        param.err_code = tonumber(err_code);
        param.err_msg = err_msg;
        local info = {};
        info.msg_type = "bp_launch_game_error";
        info.req_param = base_utility.table_to_json(param);
        result = comm_center.call_service("i_service_bp_ui_request", info);
    end;

    on_service_show_wnd = function(data)
        base_utility.log("[ui_utility]on_service_show_wnd:" ..
                base_utility.table_to_json(data));
        if not data or
            not data.win_id then
            base_utility.log("[ui_utility][on_service_show_wnd]param INVALID:" ..
                    base_utility.table_to_json(data));
            return;
        end

        if nil == data.show or
            true == data.show  then
            ui_utility.show_window(data.win_id, true);
            local pControl = ui_utility.uilib():GetControlPtr(data.win_id);
            local dlg = tolua.cast(pControl, "Tenio::ITDialog");
            if nil ~= dlg then
                dlg:BringToTop();
            end
            return;
        end

        ui_utility.show_window(data.win_id, false);
    end;

    on_service_close_wnd = function(data)
        base_utility.log("[ui_utility]on_service_close_wnd:" ..
                base_utility.table_to_json(data));
        if not data or
                not data.win_id then
            base_utility.log("[ui_utility][on_service_show_wnd]param INVALID:" ..
                    base_utility.table_to_json(data));
            return;
        end

        ui_utility.close_window(data);
    end;

    set_wnd_opts = function(win_id, opt)
        base_utility.log("[ui_utility][set_id_to_do_modal]" .. tostring(win_id));

        if not win_id or
                0 == win_id then
            base_utility.log("[ui_utility][set_id_to_do_modal]win_id is INVALID" ..
                    tostring(win_id), true);
            return;
        end;
        ui_utility.wnd_opts[win_id] = opt;
    end;

    remove_wnd_opts = function(win_id)
        base_utility.log("[ui_utility][remove_wnd_opts]" .. tostring(win_id));
        ui_utility.wnd_opts[win_id] = nil;
    end;

    show_window = function(win_id, show)
        base_utility.log("[ui_utility][show_window]" .. tostring(win_id) ..
                ", show:" .. tostring(show));

        if not win_id or
                0 == win_id then
            base_utility.log("[ui_utility][show_window]win_id is INVALID:" .. tostring(win_id) ..
                    ", show:" .. tostring(show), true);
            return;
        end

        local win_id = tonumber(win_id);

        ui_utility.show_ctrl(win_id, show);

        local opt = ui_utility.wnd_opts[win_id];

        if show and opt and "true" == tostring(opt.modal) then
            ui_utility.do_fake_modal_by_id(win_id);
        end

        if not show and win_id == ui_utility.current_modal_dlg then
            base_utility.log("[ui_utility][show_window]end fake modal:" .. tostring(win_id));
            ui_utility.end_fake_modal_by_id(win_id);
        end
    end;

    close_window = function(param)
        if not param or not param.win_id then
            base_utility.log("[ui_utility][close_window]param invalid", true);
            return;
        end

        local win_id = param.win_id;
        base_utility.log("[ui_utility][close_window]" .. tostring(win_id));

        ui_utility.show_window(win_id, false);

        if not param.delay or
            type(param.delay) ~= number or
                (param.delay == 0) then
            ui_utility.remove_ctrl(win_id);
            return;
        end;

        base_utility.log("[ui_utility][close_window]" .. tostring(win_id) ..
            ", delay:" .. tostring(param.delay));
        base_utility.add_interval("ui_utility.remove_ctrl", param.delay, win_id);
    end;

    remove_ctrl = function(ctrl_id)                                        
        if not ctrl_id then
            base_utility.log("[ui_utility][remove_ctrl]id invalid:" .. tostring(ctrl_id), true);
            return;
        end
                        
        base_utility.log("[ui_utility][remove_ctrl]" .. tostring(ctrl_id));
        layout_mgr.delay_remove_ctrl(ctrl_id);
    end;

}
